package com.windward.vii.service;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.windward.vii.common.Constants;
import com.windward.vii.common.JsonResult;
import com.windward.vii.common.ResultCode;
import com.windward.vii.config.ImConfig;
import com.windward.vii.config.PlatformConfig;
import com.windward.vii.dao.entity.AssetUserAccount;
import com.windward.vii.dao.entity.Device;
import com.windward.vii.dao.entity.User;
import com.windward.vii.dao.entity.UserToken;
import com.windward.vii.dao.mapper.AssetUserAccountMapper;
import com.windward.vii.domain.user.SignParam;
import com.windward.vii.domain.user.SignResponse;
import com.windward.vii.service.aliyun.SmsService;
import com.windward.vii.service.qq.QQAuthService;
import com.windward.vii.service.qq.bean.QQUserInfo;
import com.windward.vii.service.weixin.WeixinAuthService;
import com.windward.vii.service.weixin.bean.WeixinAccessToken;
import com.windward.vii.service.weixin.bean.WeixinUserInfo;
import com.windward.vii.service.rongcloud.RongCloudProxy;
import com.windward.vii.utils.StringUtils;
import com.windward.vii.utils.Utils;
import com.windward.vii.service.rongcloud.models.TokenResult;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.TransactionDefinition;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.support.DefaultTransactionDefinition;

import javax.annotation.Resource;
import java.util.Date;

@Component
public class SignService {

    private static final Logger logger = LoggerFactory.getLogger(SignService.class);
    @Resource
    UserTokenService userTokenService;
    @Resource
    UserService userService;
    @Autowired
    PlatformTransactionManager transactionManager;
    @Resource
    SmsService smsService;
    @Resource
    DeviceService deviceService;
    @Resource
    PlatformConfig config;
    @Resource
    AssetUserAccountMapper assetUserAccountMapper;
    @Resource
    ImConfig imConfig;
    @Resource
    WeixinAuthService weixinAuthService;
    @Resource
    QQAuthService qqAuthService;
    @Resource
    RongCloudProxy rongCloudProxy;

    public JsonResult login(SignParam param) {
        Integer loginType = param.getThirdType();
        if(loginType == null || loginType == 0) { // 手机+验证码，登录
            return loginByMobile(param);
        } else if(loginType == Constants.User.THIRD_TYPE_WEIXIN) {
            return loginByWeixin(param);
        } else if(loginType == Constants.User.THIRD_TYPE_QQ) {
            return loginByQQ(param);
        } else {
            return new JsonResult(ResultCode.PARAM_ERROR_CODE, "无法是识别的登录类型");
        }
    }

    private JsonResult loginByQQ(SignParam param) {
        if(StringUtils.isBlank(param.getQqAccessToken())) {
            return new JsonResult(ResultCode.PARAM_ERROR_CODE, "QQ登录access_token为空");
        }
        if(StringUtils.isBlank(param.getQqOpenId())) {
            return new JsonResult(ResultCode.PARAM_ERROR_CODE, "QQ登录openid为空");
        }

        User dbUser = userService.selectByThirdId(param.getQqOpenId());
        if(dbUser != null) {
            return login(param, dbUser);
        } else {
            JsonResult qqGetUserInfoJsonResult = qqAuthService.getUserInfo(param.getQqAccessToken(), param.getQqOpenId());
            if(qqGetUserInfoJsonResult.getCode() != ResultCode.SUCCESS_CODE) {
                return qqGetUserInfoJsonResult;
            }
            QQUserInfo qqUserInfo = (QQUserInfo) qqGetUserInfoJsonResult.getBody();
            User user = new User();
            user.setUsername(param.getQqOpenId());
            user.setLastLoginIp(param.getDevice().getRegisterIp());
            user.setThirdType(Constants.User.THIRD_TYPE_QQ);
            user.setThirdId(param.getQqOpenId());
            String avatar = qqUserInfo.getFigureurl_qq_2();
            if(StringUtils.isBlank(avatar)) {
                avatar = qqUserInfo.getFigureurl_qq_1();
            }
            JSONObject thirdOption = new JSONObject();
            thirdOption.put("user_info", qqUserInfo);
            thirdOption.put("access_token", param.getQqAccessToken());
            if(thirdOption.toJSONString().length() <= 2000) {
                user.setThirdOption(thirdOption.toJSONString());
            }

            AssetUserAccount userAccount = new AssetUserAccount();
            if(qqUserInfo.getGender() != null) {
                if(qqUserInfo.getGender().equals("男")) {
                    userAccount.setSex(Constants.User.SEX_MEN);
                } else if(qqUserInfo.getGender().equals("女")) {
                    userAccount.setSex(Constants.User.SEX_WOMEN);
                }
            }
            if(avatar.length() <= 300) {
                userAccount.setAvatar(avatar);
            } else {
                userAccount.setAvatar(Constants.User.DEFAULT_AVATAR_URL);
            }
            userAccount.setNickname(qqUserInfo.getNickname());
            return register(param, user, userAccount);
        }
    }

    private JsonResult loginByWeixin(SignParam param) {
        if(StringUtils.isBlank(param.getWxAuthCode())) {
            return new JsonResult(ResultCode.PARAM_ERROR_CODE, "微信登录授权Code为空");
        }

        JsonResult wxAutoJsonResult = weixinAuthService.getAccessToken(param.getWxAuthCode());
        if(wxAutoJsonResult.getCode() != ResultCode.SUCCESS_CODE) {
            return wxAutoJsonResult;
        }

        WeixinAccessToken weixinAccessToken = (WeixinAccessToken) wxAutoJsonResult.getBody();
        User dbUser = userService.selectByThirdId(weixinAccessToken.getOpenid());
        if(dbUser != null) {
            return login(param, dbUser);
        } else {
            JsonResult wxGetUserInfoJsonResult = weixinAuthService.getUserInfo(weixinAccessToken.getAccess_token(), weixinAccessToken.getOpenid());
            if(wxGetUserInfoJsonResult.getCode() != ResultCode.SUCCESS_CODE) {
                return wxGetUserInfoJsonResult;
            }
            WeixinUserInfo weixinUserInfo = (WeixinUserInfo) wxGetUserInfoJsonResult.getBody();
            User user = new User();
            user.setUsername(weixinUserInfo.getUnionid());
            user.setLastLoginIp(param.getDevice().getRegisterIp());
            user.setThirdType(Constants.User.THIRD_TYPE_WEIXIN);
            user.setThirdId(weixinAccessToken.getOpenid());
            JSONObject thirdOption = new JSONObject();
            thirdOption.put("user_info", weixinUserInfo);
            thirdOption.put("access_token", weixinAccessToken);
            if(thirdOption.toJSONString().length() <= 2000) {
                user.setThirdOption(thirdOption.toJSONString());
            }
            AssetUserAccount userAccount = new AssetUserAccount();
            if(weixinUserInfo.getHeadimgurl() != null && weixinUserInfo.getHeadimgurl().length() <= 300) {
                userAccount.setAvatar(weixinUserInfo.getHeadimgurl());
            } else {
                userAccount.setAvatar(Constants.User.DEFAULT_AVATAR_URL);
            }
            userAccount.setSex(weixinUserInfo.getSex());
            userAccount.setNickname(weixinUserInfo.getNickname());
            userAccount.setCountry(weixinUserInfo.getCountry());
            userAccount.setProvince(weixinUserInfo.getProvince());
            userAccount.setCity(weixinUserInfo.getCity());

            return register(param, user, userAccount);
        }
    }

    private JsonResult loginByMobile(SignParam param) {
        // mobile not blank
        if(StringUtils.isBlank(param.getMobile())) {
            return new JsonResult(ResultCode.PARAM_ERROR_CODE, ResultCode.INPUT_MOBILE_NUMBER_MSG);
        }
        // mobile number
        if (!Utils.checkMobile(param.getMobile())) {
            return new JsonResult(ResultCode.PARAM_ERROR_CODE, ResultCode.CHECK_MOBILE_NUMBER_MSG);
        }
        // verify code not blank
        if(StringUtils.isBlank(param.getVerifyCode())) {
            return new JsonResult(ResultCode.VERIFY_CODE_ERROR_CODE, ResultCode.VERIFY_FAIL_MSG);
        }
        // verify sms
//        String verifyCode = redisManager.get(RedisConstants.MOBILE + param.getMobile());
        String verifyCode = smsService.getVerifyCodeByMobile(param.getMobile());
        if (StringUtils.isBlank(verifyCode)) {
            return new JsonResult(ResultCode.VERIFYCODE_OUT_TIME_CODE, ResultCode.VERIFYCODE_OUT_TIME_MSG);
        }
        if(!param.getVerifyCode().equals(verifyCode)) {
            return new JsonResult(ResultCode.VERIFYCODE_EXIST, ResultCode.VERIFYCODE_EXIST_MSG);
        }
        // check user is exist
        User dbUser = userService.selectUserByMobile(param.getMobile());
        if(dbUser == null) {
            User user = new User();
            user.setMobile(param.getMobile());
            user.setUsername(param.getMobile());

            AssetUserAccount userAccount = new AssetUserAccount();
            int random = (int)(Math.random() * 10000000);
            userAccount.setNickname("手机用户" + random);
            return register(param, user, userAccount);
        } else {
            return login(param, dbUser);
        }
    }

    private JsonResult login(SignParam param, User dbUser) {
        AssetUserAccount assetUserAccount = assetUserAccountMapper.selectByUid(dbUser.getId());
        if(assetUserAccount.getStatus() != null && assetUserAccount.getStatus() == Constants.User.STATUS_LOCKED) {
            return JsonResult.error("你的账号因违规已被禁用");
        }

        DefaultTransactionDefinition def = new DefaultTransactionDefinition();
        def.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);
        TransactionStatus status = transactionManager.getTransaction(def);
        boolean isLoginSuccess = false;
        try {
            // update device
            Device device = param.getDevice();
            deviceService.insertOrUpdateLoginDevice(dbUser.getId(), device);

            // update user
            User updateUser = new User();
            updateUser.setId(dbUser.getId());
            updateUser.setLastLoginIp(param.getDevice().getRegisterIp());
            updateUser.setLastLoginTime(new Date());
            updateUser.setLastLoginDeviceId(device.getId());
            if(device.getDeviceToken() != null) {
                updateUser.setDeviceToken(device.getDeviceToken());
            }
            if(!userService.updateByPrimaryKeySelective(updateUser)) {
                logger.error("login, userService.updateUserById error, updateUser={}", JSON.toJSONString(updateUser));
                return new JsonResult(ResultCode.DB_ERROR_CODE, "更新用户信息失败，请重试");
            }

            // del old token by mobile
            if(!userTokenService.deleteTokenByUid(dbUser.getId())) {
                logger.error("login, userService.deleteTokenByUid error, uid={}", dbUser.getId());
                return new JsonResult(ResultCode.DB_ERROR_CODE, "生成Token失败，请重试");
            }

            // gen token
            UserToken userToken = new UserToken();
            userToken.setToken(Utils.genToken());
            userToken.setUid(dbUser.getId());
            userToken.setTerminalType(param.getDevice().getTerminalType());
            userToken.setExpireTime(getTokenExpireTime());
            if(!userTokenService.insert(userToken)) {
                logger.error("login, userTokenService.insert error, userToken=" + JSON.toJSONString(userToken));
                return new JsonResult(ResultCode.DB_ERROR_CODE, "生成Token失败，请重试");
            }

            isLoginSuccess = true;

            JsonResult response = new JsonResult(ResultCode.SUCCESS_CODE, "登录成功");
            SignResponse loginToken = new SignResponse();
            loginToken.setToken(userToken.getToken());
            loginToken.setImToken(dbUser.getImToken());
            loginToken.setUserId(dbUser.getId());
            loginToken.setStatus(assetUserAccount.getInfoStatus());
            response.setBody(loginToken);
            return response;
        } catch (Exception e) {
            e.printStackTrace();
            return new JsonResult(ResultCode.DB_ERROR_CODE, "登录失败，请重试");
        } finally {
            if(isLoginSuccess) {
                transactionManager.commit(status);
            } else {
                transactionManager.rollback(status);
            }
        }
    }

    private JsonResult register(SignParam param, User user, AssetUserAccount userAccount) {
        user.setRegisterIp(param.getDevice().getRegisterIp());
        user.setLastLoginIp(param.getDevice().getRegisterIp());
        user.setLastLoginTime(new Date());
        user.setRegisterTime(new Date());
        DefaultTransactionDefinition def = new DefaultTransactionDefinition();
        def.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);
        TransactionStatus status = transactionManager.getTransaction(def);
        boolean isRegisterSuccess = false;
        try {
            // save user
            if(!userService.insertAndGetId(user)) {
                logger.error("register, saveSecurityUser error, RegisterParam=" + param.toString());
                return new JsonResult(ResultCode.DB_ERROR_CODE, "保存用户信息失败，请重试");
            }

            // save user device
            Device device = param.getDevice();
            JsonResult insertDeviceResult = deviceService.insertOrUpdateLoginDevice(user.getId(), device);
            if(insertDeviceResult.getCode() != ResultCode.SUCCESS_CODE) {
                logger.error("register, userDeviceMapper.insert error, RegisterParam=" + param.toString());
                return new JsonResult(ResultCode.DB_ERROR_CODE, "保存用户设备信息失败，请重试");
            }

            // gen user asset account
            userAccount.setStatus(Constants.User.STATUS_ACTIVE);
            userAccount.setInfoStatus(Constants.User.INFO_STATUS_INIT);
            userAccount.setUid(user.getId());
            if(assetUserAccountMapper.insertSelective(userAccount) < 1) {
                logger.error("register, assetUserAccountMapper.insert error, RegisterParam=" + param.toString());
                return new JsonResult(ResultCode.DB_ERROR_CODE, "更新用户信息失败，请重试");
            }

            // gen token
            UserToken securityToken = new UserToken();
            securityToken.setToken(Utils.genToken());
            securityToken.setUid(user.getId());
            securityToken.setTerminalType(device.getTerminalType());
            securityToken.setExpireTime(getTokenExpireTime());
            if(!userTokenService.insert(securityToken)) {
                logger.error("register, userService.updateUserById error, RegisterParam=" + param.toString());
                return new JsonResult(ResultCode.DB_ERROR_CODE, "生成Token失败，请重试");
            }

            // update user (lastLoginDeviceId, imToken)
            User updateUser = new User();
            updateUser.setId(user.getId());
            updateUser.setLastLoginDeviceId(device.getId());
            if(device.getDeviceToken() != null) {
                updateUser.setDeviceToken(device.getDeviceToken());
            }
            TokenResult tokenResult = rongCloudProxy.getInstance().user.getToken(String.valueOf(user.getId()), userAccount.getNickname(), imConfig.getDefaultAvatar());
            if(tokenResult == null || StringUtils.isEmpty(tokenResult.getToken())) {
                return new JsonResult(ResultCode.DB_ERROR_CODE, tokenResult.getErrorMessage());
            }
            updateUser.setImToken(tokenResult.getToken());

            if(!userService.updateByPrimaryKeySelective(updateUser)) {
                logger.error("register, userService.updateUserById error, RegisterParam=" + param.toString());
                return new JsonResult(ResultCode.DB_ERROR_CODE, "更新用户信息是吧，请重试");
            }

            isRegisterSuccess = true;

            JsonResult response = new JsonResult(ResultCode.SUCCESS_CODE, "登录成功");
            SignResponse loginToken = new SignResponse();
            loginToken.setToken(securityToken.getToken());
            loginToken.setImToken(tokenResult.getToken());
            loginToken.setUserId(user.getId());
            loginToken.setStatus(Constants.User.INFO_STATUS_INIT);
            response.setBody(loginToken);
            return response;
        } catch (Exception e) {
            e.printStackTrace();
            return new JsonResult(ResultCode.DB_ERROR_CODE, "登录(Reg)失败，请重试");
        } finally {
            if(isRegisterSuccess) {
                transactionManager.commit(status);
            } else {
                transactionManager.rollback(status);
            }
        }
    }


    /**
     * 返回登录用户的token过期时间
     * @return Date
     */
    private Date getTokenExpireTime(){
        return new Date(System.currentTimeMillis() + config.getTokenExpireTime() * 24 * 3600 *1000);
    }

    public JsonResult logout(String token) {
        return userTokenService.logoutByToken(token);
    }

}

